﻿Public Class Volume
  Public Event MuteChanged(ByVal enabled As Boolean)
  Public Event VolumeChanged(ByVal level As Byte)

  Private _dpiFactor As Single = 0.0F

  Private _mute As Boolean = False
  Private _volume As Byte = 100

  Private _bmpEmpty As Bitmap = Nothing
  Private _bmpFull As Bitmap = Nothing

  Private _ySegments(10) As Integer
  Private _mouseIsDown As Boolean = False

  Public Sub New()
    Me.InitializeComponent()
    Me._dpiFactor = Me.AutoScaleDimensions.Height / 192.0F
    Me.MenuItemMute.Text = Language.GetLanguageString("Mute")
    Me.MenuItemClose.Text = Language.GetLanguageString("Close")
  End Sub

  Private Sub Volume_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
    Me.TimerClose.Enabled = True
  End Sub

  Public Sub SetImages(ByVal empty As Bitmap, ByVal full As Bitmap)
    Me._bmpEmpty = empty
    Me._bmpFull = full
  End Sub

  Public Property Mute() As Boolean
    Get
      Return Me._mute
    End Get
    Set(ByVal value As Boolean)
      Me._mute = value
      If Me._mute Then
        Me.MenuItemMute.Text = Language.GetLanguageString("Unmute")
      Else
        Me.MenuItemMute.Text = Language.GetLanguageString("Mute")
      End If
      Me.InvalidateSmart()
    End Set
  End Property

  Public Property Volume() As Byte
    Get
      Return Me._volume
    End Get
    Set(ByVal value As Byte)
      Me._volume = value
    End Set
  End Property

  Private Sub MenuItemClose_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemClose.Click
    Me.TimerClose.Enabled = False
    Me.Close()
  End Sub

  Private Sub MenuItemMute_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MenuItemMute.Click
    Me.TimerClose.Enabled = False

    Me.Mute = Not Me.Mute
    RaiseEvent MuteChanged(Me.Mute)

    Me.TimerClose.Enabled = True
  End Sub

  Private Sub Me_Closed() Handles Me.Closed
    Me.TimerClose.Enabled = False

    Try
      If Me._bmpEmpty IsNot Nothing Then
        Me._bmpEmpty.Dispose()
        Me._bmpEmpty = Nothing
      End If
    Catch
    End Try

    Try
      If Me._bmpFull IsNot Nothing Then
        Me._bmpFull.Dispose()
        Me._bmpFull = Nothing
      End If
    Catch
    End Try
  End Sub

  Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
    Static Dim bufWidth As Integer = 0
    Static Dim bufHeight As Integer = 0
    Static Dim buffer As Bitmap = Nothing
    Static Dim bufRect As Rectangle = Rectangle.Empty
    Static Dim bb As New SolidBrush(Me.BackColor)

    If Me.Height > 0 AndAlso Me.Width > 0 Then
      If Me.Width <> bufWidth OrElse Me.Height <> bufHeight Then
        ' Setup back buffer and state variables
        bufWidth = Me.Width
        bufHeight = Me.Height

        ' Dispose of the old buffer we don't need anymore.
        If buffer IsNot Nothing Then
          buffer.Dispose()
        End If

        ' Buffer
        buffer = New Bitmap(bufWidth, bufHeight)
        bufRect = New Rectangle(0, 0, bufWidth, bufHeight)
      End If

      ' Create temporary graphics buffer
      Dim g As Graphics = Graphics.FromImage(buffer)

      ' Clear the background
      g.FillRectangle(bb, 0, 0, Me.Width, Me.Height)

      ' Draw volume segments
      Dim segmentHeight As Integer = Me._bmpEmpty.Height * Me._dpiFactor
      Dim segmentWidth As Integer = Me._bmpEmpty.Width * Me._dpiFactor
      Dim spacing As Integer = (bufHeight - (segmentHeight * 11)) / 10

      If spacing > segmentHeight Then spacing = segmentHeight
      If spacing < 0 Then spacing = 0

      Dim centerOffset As Integer = (bufWidth - segmentWidth) >> 1
      Dim padding As Integer = (bufHeight - ((spacing * 9) + (segmentHeight * 10))) >> 1
      Dim vol10 As Byte = Me._volume / 10
      Dim volCount As Byte = 1

      Me._ySegments(10) = (((segmentHeight + spacing) * 10) + padding) - spacing

      For i As Integer = 9 To 0 Step -1
        Dim curTop = ((segmentHeight + spacing) * i) + padding
        Me._ySegments(i) = curTop - spacing

        If Not Me._mute AndAlso vol10 >= volCount Then
          g.DrawImage(Me._bmpFull, New Rectangle(centerOffset, curTop, segmentWidth, segmentHeight), New Rectangle(0, 0, Me._bmpEmpty.Width, Me._bmpEmpty.Height), GraphicsUnit.Pixel)
        Else
          g.DrawImage(Me._bmpEmpty, New Rectangle(centerOffset, curTop, segmentWidth, segmentHeight), New Rectangle(0, 0, Me._bmpEmpty.Width, Me._bmpEmpty.Height), GraphicsUnit.Pixel)
        End If

        volCount += 1
      Next

      ' Copy back to the front buffer
      g.Dispose()
      e.Graphics.DrawImage(buffer, 0, 0)
    End If
  End Sub

  Protected Overrides Sub OnPaintBackground(ByVal e As System.Windows.Forms.PaintEventArgs)
  End Sub

  Protected Overrides Sub OnMouseDown(ByVal e As System.Windows.Forms.MouseEventArgs)
    Me.TimerClose.Enabled = False
    Me._mouseIsDown = True
    Me.ProcessMouse(e.X, e.Y)
    MyBase.OnMouseDown(e)
  End Sub

  Protected Overrides Sub OnMouseMove(ByVal e As System.Windows.Forms.MouseEventArgs)
    If Me._mouseIsDown Then
      Me.ProcessMouse(e.X, e.Y)
    End If
    MyBase.OnMouseMove(e)
  End Sub

  Protected Overrides Sub OnMouseUp(ByVal e As System.Windows.Forms.MouseEventArgs)
    If Me._mouseIsDown Then
      Me.ProcessMouse(e.X, e.Y)
    End If
    Me._mouseIsDown = False
    MyBase.OnMouseUp(e)

    Me.TimerClose.Enabled = True
  End Sub

  Private Sub ProcessMouse(ByVal x As Integer, ByVal y As Integer)
    Dim lastVolume As Byte = Me.Volume

    Dim v As Integer = 0

    For i = 10 To 0 Step -1
      If y >= Me._ySegments(i) Then
        Exit For
      End If
      v += 1
    Next

    Me._volume = v * 10

    If Me._volume <> lastVolume Then
      RaiseEvent VolumeChanged(Me._volume)
    ElseIf Me.Mute Then
      RaiseEvent MuteChanged(False)
    End If

    Me.Mute = False

    Me.InvalidateSmart()
  End Sub

  Public Sub InvalidateSmart()
    Me.TimerInvalidate.Enabled = True
  End Sub

  Private Sub TimerInvalidate_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerInvalidate.Tick
    Me.TimerInvalidate.Enabled = False
    Me.Invalidate()
  End Sub

  Private Sub TimerClose_Tick(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TimerClose.Tick
    Me.Close()
  End Sub
End Class